package org.example.service;

import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import jakarta.annotation.PostConstruct;
import okhttp3.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
public class VoiceService {
    private static final Logger logger = LoggerFactory.getLogger(VoiceService.class);
    private final OkHttpClient httpClient = new OkHttpClient.Builder()
            .connectTimeout(10, TimeUnit.SECONDS)
            .readTimeout(30, TimeUnit.SECONDS) // 增加读取超时到30秒
            .writeTimeout(10, TimeUnit.SECONDS)
            .build();
    private final Gson gson = new Gson();

    // 有效的音色ID列表
    private List<String> availableSpeakers = new ArrayList<>(Arrays.asList("中文女", "中文男", "英文女", "英文男"));
    // 后备音色ID，确保在配置中的音色无效时有备选项
    private static final String FALLBACK_SPEAKER = "中文女";
    // 静默音频文件，在TTS服务失败时返回
    private byte[] fallbackAudio;
    // 超时错误音频文件，与静默音频不同，包含特殊标记
    private byte[] timeoutAudio;

    @Value("${cosyvoice.url:http://localhost:8001/api/v1/tts/sft}")
    private String cosyVoiceUrl;

    @Value("${cosyvoice.list.url:http://localhost:8001/api/v1/list_speakers}")
    private String cosyVoiceListUrl;

    @Value("${cosyvoice.default.speaker:中文女}")
    private String defaultSpeaker;

    @Value("${cosyvoice.enabled:true}")
    private boolean cosyVoiceEnabled;

    /**
     * 初始化，加载可用音色列表和后备音频
     */
    @PostConstruct
    public void init() {
        // 加载静默音频作为后备方案
        try {
            Resource resource = new ClassPathResource("static/silent.wav");
            if (resource.exists()) {
                try (InputStream is = resource.getInputStream()) {
                    ByteArrayOutputStream buffer = new ByteArrayOutputStream();
                    int nRead;
                    byte[] data = new byte[16384];
                    while ((nRead = is.read(data, 0, data.length)) != -1) {
                        buffer.write(data, 0, nRead);
                    }
                    fallbackAudio = buffer.toByteArray();
                    logger.info("已加载静默音频作为后备方案");
                }
            } else {
                // 生成一个简单的空WAV文件
                fallbackAudio = generateSilentWav();
                logger.info("已生成静默音频作为后备方案");
            }
        } catch (Exception e) {
            logger.warn("加载静默音频失败，将在需要时生成: {}", e.getMessage());
            fallbackAudio = generateSilentWav();
        }

        // 创建超时错误音频，这是一个特殊的WAV文件
        // 它包含了一个特殊的头部标记，前端可以通过这个标记识别出超时错误
        timeoutAudio = generateErrorWav("TTS_TIMEOUT");
        logger.info("已生成超时错误音频");

        // 如果CosyVoice启用，则尝试获取可用音色列表
        if (cosyVoiceEnabled) {
            try {
                fetchAvailableSpeakers();
            } catch (Exception e) {
                logger.warn("获取CosyVoice可用音色列表失败: {}，将使用默认音色列表", e.getMessage());
            }
        }

        // 确保defaultSpeaker是有效的，否则使用后备音色
        if (!availableSpeakers.contains(defaultSpeaker)) {
            logger.warn("配置的默认音色 '{}' 不可用，将使用后备音色 '{}'", defaultSpeaker, FALLBACK_SPEAKER);
            defaultSpeaker = FALLBACK_SPEAKER;
        }
    }

    /**
     * 生成一个简单的静默WAV文件，作为后备方案
     */
    private byte[] generateSilentWav() {
        // 简单的静默WAV文件，包含WAV头和2秒静默数据
        // 采样率16000Hz，16位，单声道
        int sampleRate = 16000;
        int bitsPerSample = 16;
        int channels = 1;
        int durationInSeconds = 2;

        // 计算数据大小
        int dataSize = sampleRate * durationInSeconds * channels * bitsPerSample / 8;
        int fileSize = 36 + dataSize;

        // 创建WAV文件字节数组
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            // RIFF头
            out.write("RIFF".getBytes());
            writeInt(out, fileSize);
            out.write("WAVE".getBytes());

            // fmt子块
            out.write("fmt ".getBytes());
            writeInt(out, 16); // 子块大小
            writeShort(out, (short) 1); // 音频格式(PCM=1)
            writeShort(out, (short) channels); // 声道数
            writeInt(out, sampleRate); // 采样率
            writeInt(out, sampleRate * channels * bitsPerSample / 8); // 字节率
            writeShort(out, (short) (channels * bitsPerSample / 8)); // 块对齐
            writeShort(out, (short) bitsPerSample); // 每样本位数

            // data子块
            out.write("data".getBytes());
            writeInt(out, dataSize); // 数据大小

            // 写入静默数据(所有样本值为0)
            byte[] silentData = new byte[dataSize];
            out.write(silentData);

            return out.toByteArray();
        } catch (IOException e) {
            logger.error("生成静默WAV文件失败: {}", e.getMessage());
            // 返回一个小的空字节数组作为最后的后备方案
            return new byte[44]; // WAV头的大小
        }
    }

    /**
     * 生成特殊的错误WAV文件，包含错误类型标记
     *
     * @param errorType 错误类型标识
     * @return 错误WAV文件字节数组
     */
    private byte[] generateErrorWav(String errorType) {
        // 创建一个简单的WAV文件，但在文件头部添加特殊标记
        int sampleRate = 16000;
        int bitsPerSample = 16;
        int channels = 1;
        int durationInSeconds = 1; // 错误音频可以短一些

        // 计算数据大小
        int dataSize = sampleRate * durationInSeconds * channels * bitsPerSample / 8;

        // 创建WAV文件字节数组
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            // 首先添加错误标记 - 放在文件最前面，确保易于检测
            String errorHeader = "ERROR_AUDIO:" + errorType;
            out.write(errorHeader.getBytes(StandardCharsets.UTF_8));

            // 标准WAV文件结构
            // RIFF头
            out.write("RIFF".getBytes());
            writeInt(out, 36 + dataSize); // 文件大小
            out.write("WAVE".getBytes());

            // fmt子块
            out.write("fmt ".getBytes());
            writeInt(out, 16); // 子块大小
            writeShort(out, (short) 1); // 音频格式(PCM=1)
            writeShort(out, (short) channels); // 声道数
            writeInt(out, sampleRate); // 采样率
            writeInt(out, sampleRate * channels * bitsPerSample / 8); // 字节率
            writeShort(out, (short) (channels * bitsPerSample / 8)); // 块对齐
            writeShort(out, (short) bitsPerSample); // 每样本位数

            // data子块
            out.write("data".getBytes());
            writeInt(out, dataSize); // 数据大小

            // 写入静默数据(所有样本值为0)
            byte[] silentData = new byte[dataSize];
            out.write(silentData);

            return out.toByteArray();
        } catch (IOException e) {
            logger.error("生成错误WAV文件失败: {}", e.getMessage());
            // 返回一个小的空字节数组作为最后的后备方案
            return new byte[44]; // WAV头的大小
        }
    }

    /**
     * 文本转语音
     *
     * @param text      要转换的文本
     * @param speakerId 发音人ID
     * @return 包含语音数据的字节数组
     */
    public byte[] textToSpeech(String text, String speakerId) {
        // 如果CosyVoice服务禁用，则直接返回静默音频
        if (!cosyVoiceEnabled) {
            logger.info("CosyVoice服务已禁用，返回静默音频");
            return fallbackAudio;
        }

        // 文本为空时直接返回静默音频
        if (text == null || text.trim().isEmpty()) {
            logger.warn("要转换的文本为空，返回静默音频");
            return fallbackAudio;
        }

        // 预处理文本，优化TTS效果
        text = preprocessText(text);

        if (speakerId == null || speakerId.isEmpty()) {
            speakerId = defaultSpeaker;
            logger.info("未指定音色ID，使用默认音色: {}", defaultSpeaker);
        }

        // 确保使用有效的音色ID
        if (!availableSpeakers.contains(speakerId)) {
            logger.warn("请求的音色ID '{}' 不可用，将使用默认音色 '{}'", speakerId, defaultSpeaker);
            speakerId = defaultSpeaker;
        }

        try {
            logger.info("调用CosyVoice API，文本长度: {} 字符, 说话人: {}", text.length(), speakerId);
            logger.debug("TTS文本内容: {}",
                    text.substring(0, Math.min(50, text.length())) + (text.length() > 50 ? "..." : ""));

            // 使用表单数据构建请求体
            FormBody.Builder formBuilder = new FormBody.Builder(StandardCharsets.UTF_8)
                    .add("text", text)
                    .add("speaker_id", speakerId)
                    .add("stream", "false")
                    .add("speed", "1.0");

            RequestBody requestBody = formBuilder.build();

            // 创建POST请求
            Request request = new Request.Builder()
                    .url(cosyVoiceUrl)
                    .post(requestBody)
                    .build();

            logger.debug("发送Form表单请求到CosyVoice");

            try (Response response = httpClient.newCall(request).execute()) {
                if (!response.isSuccessful()) {
                    String errorBody = response.body() != null ? response.body().string() : "无响应内容";
                    logger.error("CosyVoice服务返回错误: {}, 响应内容: {}", response.code(), errorBody);
                    // 失败时返回静默音频，而不是抛出异常
                    logger.info("CosyVoice调用失败，返回静默音频");
                    return fallbackAudio;
                }

                if (response.body() == null) {
                    logger.error("CosyVoice服务返回空响应体");
                    return fallbackAudio;
                }

                byte[] audioData = response.body().bytes();
                logger.info("成功从CosyVoice获取音频数据，大小: {} 字节", audioData.length);

                // 验证数据有效性
                if (audioData.length < 44) { // 最小WAV头部大小
                    logger.error("CosyVoice返回的音频数据太小（{}字节），可能无效", audioData.length);
                    return fallbackAudio;
                }

                // 返回二进制音频数据
                return audioData;
            }
        } catch (java.net.SocketTimeoutException e) {
            logger.error("调用CosyVoice API超时: {}", e.getMessage());
            // 超时特殊处理，返回超时错误音频
            logger.info("CosyVoice调用超时，返回超时错误音频");
            return timeoutAudio;
        } catch (Exception e) {
            logger.error("调用CosyVoice API失败: {}", e.getMessage());
            // 其他异常返回普通静默音频
            logger.info("CosyVoice调用异常，返回静默音频");
            return fallbackAudio;
        }
    }

    /**
     * 预处理文本，优化TTS效果
     * 
     * @param text 原始文本
     * @return 处理后的文本
     */
    private String preprocessText(String text) {
        if (text == null) {
            return "";
        }

        // 移除多余的空白字符
        text = text.trim().replaceAll("\\s+", " ");

        // 处理特殊符号，确保TTS能正确读出
        text = text.replace("#", "号");
        text = text.replace("%", "百分之");
        text = text.replace("&", "和");

        // 处理英文和中文之间的空格
        text = text.replaceAll("([a-zA-Z])([\\u4e00-\\u9fa5])", "$1 $2");
        text = text.replaceAll("([\\u4e00-\\u9fa5])([a-zA-Z])", "$1 $2");

        // 处理常见的缩写和特殊词汇
        text = text.replaceAll("(?i)\\bAI\\b", "人工智能");
        text = text.replaceAll("(?i)\\bTTS\\b", "语音合成");
        text = text.replaceAll("(?i)\\bASR\\b", "语音识别");

        // 限制文本长度，防止过长导致TTS失败
        final int maxLength = 2000; // 设置最大长度
        if (text.length() > maxLength) {
            logger.warn("文本超过最大长度({})，将被截断", maxLength);
            text = text.substring(0, maxLength);
            // 添加省略标记
            text += "......";
        }

        return text;
    }

    /**
     * 使用默认说话人将文本转换为语音
     * 
     * @param text 需要转换的文本
     * @return 语音数据的字节数组
     */
    public byte[] textToSpeech(String text) {
        return textToSpeech(text, null);
    }

    /**
     * 获取可用的音色列表
     */
    private void fetchAvailableSpeakers() {
        try {
            Request request = new Request.Builder()
                    .url(cosyVoiceListUrl)
                    .get()
                    .build();

            try (Response response = httpClient.newCall(request).execute()) {
                if (response.isSuccessful() && response.body() != null) {
                    String responseBody = response.body().string();
                    JsonObject jsonResponse = gson.fromJson(responseBody, JsonObject.class);

                    if (jsonResponse.has("speakers")) {
                        JsonArray speakersArray = jsonResponse.getAsJsonArray("speakers");
                        List<String> speakers = new ArrayList<>();

                        for (JsonElement element : speakersArray) {
                            speakers.add(element.getAsString());
                        }

                        if (!speakers.isEmpty()) {
                            availableSpeakers = speakers;
                            logger.info("成功获取CosyVoice可用音色列表: {}", availableSpeakers);
                        }
                    }
                }
            }
        } catch (Exception e) {
            logger.error("获取CosyVoice可用音色列表失败: {}", e.getMessage());
        }
    }

    /**
     * 将整数写入字节输出流，按小端字节序排列
     */
    private void writeInt(ByteArrayOutputStream out, int value) throws IOException {
        out.write(value & 0xFF);
        out.write((value >> 8) & 0xFF);
        out.write((value >> 16) & 0xFF);
        out.write((value >> 24) & 0xFF);
    }

    /**
     * 将短整数写入字节输出流，按小端字节序排列
     */
    private void writeShort(ByteArrayOutputStream out, short value) throws IOException {
        out.write(value & 0xFF);
        out.write((value >> 8) & 0xFF);
    }
}